﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NHibernate;
using LIMS.Entities;
using NHibernate.Criterion;
//using NHibernate;
//using NHibernate.Expression;
using Castle.Core.Logging;
using System.Data;

namespace LIMS.DataAccess
{
    public abstract class AbstractDBA<T> : IDBA<T>
    {
        protected readonly string SessionFactoryConfigPath;
        /// <param name="sessionFactoryConfigPath">Fully qualified path of the session factory's config file</param>
        ///Constructor     
        public AbstractDBA(string sessionFactoryConfigPath, ILogger oLogger)
        {
            SessionFactoryConfigPath = sessionFactoryConfigPath;
            this.logger = oLogger;
        }
        /// <summary>
        /// Get Entity by ID
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public T GetById(int id)
        {
            T entity;
            entity = (T)NHibernateSession.Load(persitentType, id);
            return entity;
        }

        /// <summary>
        /// Loads every instance of the requested type with no filtering.
        /// </summary>
        public List<T> GetAll()
        {
            return GetByCriteria();
        }

        /// <summary>
        /// Get By Criteria
        /// </summary>
        /// <param name="maxResults"></param>
        /// <param name="criterionList"></param>
        /// <returns></returns>
        public List<T> GetByCriteria(int maxResults, params ICriterion[] criterionList)
        {
            ICriteria criteria = NHibernateSession.CreateCriteria(persitentType).SetMaxResults(maxResults);
            foreach (ICriterion criterion in criterionList)
                criteria.Add(criterion);

            return criteria.List<T>() as List<T>;
        }

        /// <summary>
        /// Get Unique By Criteria
        /// </summary>
        /// <param name="criterionList"></param>
        /// <returns></returns>
        public T GetUniqueByCriteria(params ICriterion[] criterionList)
        {
            ICriteria criteria = null;
            try
            {
                 criteria = NHibernateSession.CreateCriteria(persitentType);

                foreach (ICriterion criterion in criterionList)
                    criteria.Add(criterion);
                return criteria.UniqueResult<T>();
            }
            catch (Exception ex)
            {
                return criteria.UniqueResult<T>();
            }
           
        }

        /// <summary>
        /// Get All
        /// </summary>
        /// <param name="maxResults"></param>
        /// <returns></returns>
        public List<T> GetAll(int maxResults)
        {
            return GetByCriteria(maxResults);
        }

        /// <summary>
        /// Loads every instance of the requested type using the supplied <see cref="ICriterion" />.
        /// If no <see cref="ICriterion" /> is supplied, this behaves like <see cref="GetAll" />.
        /// </summary>
        public List<T> GetByCriteria(params ICriterion[] criterion)
        {
            ICriteria criteria = NHibernateSession.CreateCriteria(persitentType);

            foreach (ICriterion criterium in criterion)
            {
                criteria.Add(criterium);
            }

            return criteria.List<T>() as List<T>;
        }

        public List<T> GetByExample(T exampleInstance, params string[] propertiesToExclude)
        {
            ICriteria criteria = NHibernateSession.CreateCriteria(persitentType);
            Example example = Example.Create(exampleInstance);
            foreach (string propertyToExclude in propertiesToExclude)
            {
                example.ExcludeProperty(propertyToExclude);
            }
            criteria.Add(example);
            return criteria.List<T>() as List<T>;
        }

        /// <summary>
        /// Looks for a single instance using the example provided.
        /// </summary>
        /// <exception cref="NonUniqueResultException" />
        public T GetUniqueByExample(T exampleInstance, params string[] propertiesToExclude)
        {
            List<T> foundList = GetByExample(exampleInstance, propertiesToExclude);

            if (foundList.Count > 1)
            {
                throw new NonUniqueResultException(foundList.Count);
            }

            if (foundList.Count > 0)
            {
                return foundList[0];
            }
            else
            {
                return default(T);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="query"></param>
        /// <returns></returns>
        public List<T> GetByQuery(string query)
        {
            return GetByQuery(defaultMaxResults, query);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="maxResults"></param>
        /// <param name="query"></param>
        /// <returns></returns>
        public List<T> GetByQuery(int maxResults, string query)
        {
            IQuery iQuery = NHibernateSession.CreateQuery(query).SetMaxResults(maxResults);
            return iQuery.List<T>() as List<T>;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sSortColumn"></param>
        /// <param name="sDirection"></param>
        /// <returns></returns>
        public List<T> GetAllSorted(string sSortColumn, string sDirection)
        {
            if (string.IsNullOrEmpty(sSortColumn))
                return GetAll();
            if (string.IsNullOrEmpty(sDirection))
                sDirection = "ASC";

            string sQuery = "from " + persitentType.Name + " oT where Status = 'A' order by oT." + sSortColumn + " " + sDirection;
            IQuery iQuery = NHibernateSession.CreateQuery(sQuery).SetMaxResults(defaultMaxResults);
            return iQuery.List<T>() as List<T>;
        }


        /// <summary>
        /// 
        /// </summary>
        /// <param name="query"></param>
        /// <returns></returns>
        public T GetUniqueByQuery(string query)
        {
            IQuery iQuery = NHibernateSession.CreateQuery(query);
            return iQuery.UniqueResult<T>();
        }

        /// <summary>
        /// For entities that have assigned ID's, you must explicitly call Save to add a new one.
        /// See http://www.hibernate.org/hib_docs/reference/en/html/mapping.html#mapping-declaration-id-assigned.
        /// </summary>
        public T Save(T entity)
        {
            NHibernateSession.Save(entity);
            return entity;
        }

        public bool QuickSaveOrUpdate(ref T entity)
        {
            try
            {
                NHibernateSession.SaveOrUpdate(entity);
                CommitChanges();
                return true;
            }
            catch (Exception ex)
            {
                logger.Error("Error while saving/updating entity", ex);
                return false;
            }
        }

        public bool QuickDelete(T entity)
        {
            try
            {
                NHibernateSession.Delete(entity);
                CommitChanges();
                return true;
            }
            catch (Exception ex)
            {
                logger.Error("Error occured while saving entity", ex);
                return false;
            }
        }



        /// <summary>
        /// For entities with automatatically generated IDs, such as identity, SaveOrUpdate may 
        /// be called when saving a new entity.  SaveOrUpdate can also be called to _update_ any 
        /// entity, even if its ID is assigned.
        /// </summary>
        public T SaveOrUpdate(T entity)
        {
            NHibernateSession.SaveOrUpdate(entity);
            return entity;
        }

        public bool Delete(T entity)
        {
            try
            {
                NHibernateSession.Delete(entity);
                return true;
            }
            catch (Exception ex)
            {
                logger.Error("Error occured while deleting entity", ex);
                return false;
            }
        }
        /// <summary>
        /// Commits changes regardless of whether there's an open transaction or not
        /// </summary>
        public void CommitChanges()
        {
            if (NHibernateSessionManager.Instance.HasOpenTransactionOn(SessionFactoryConfigPath))
            {
                NHibernateSessionManager.Instance.CommitTransactionOn(SessionFactoryConfigPath); // What happens if this delete errors???
            }
            else
            {
                // If there's no transaction, just flush the changes
                NHibernateSessionManager.Instance.GetSessionFrom(SessionFactoryConfigPath).Flush(); // What happens if this delete errors???
            }
        }

        /// <summary>
        /// Exposes the ISession used within the DAO.
        /// </summary>
        protected ISession NHibernateSession
        {
            get
            {
                return NHibernateSessionManager.Instance.GetSessionFrom(SessionFactoryConfigPath);
            }

        }
        /// <summary>
        /// Loads every instance of the requested type using the supplied <see cref="ICriterion" />.
        /// If no <see cref="ICriterion" /> is supplied, this behaves like <see cref="GetAll" />.
        /// </summary>
        public List<T> GetSortedByCriteria(Order sortOrderList, params ICriterion[] criterion)
        {
            ICriteria criteria = NHibernateSession.CreateCriteria(persitentType);

            foreach (ICriterion criterium in criterion)
            {
                criteria.Add(criterium);
            }
            if (sortOrderList != null)
            {
                //reach (Order ordr in sortOrderList)
                // {
                criteria.AddOrder(sortOrderList);
                // }
            }

            return criteria.List<T>() as List<T>;
        }

        ///// <summary>
        ///// Loads every instance of the requested type using the supplied <see cref="ICriterion" />.
        ///// If no <see cref="ICriterion" /> is supplied, this behaves like <see cref="GetAll" />.
        ///// </summary>
        //public List<T> GetSortedByCriteria(List<Order> sortOrderList, params ICriterion[] criterion)
        //{
        //    ICriteria criteria = NHibernateSession.CreateCriteria(persitentType);

        //    foreach (ICriterion criterium in criterion)
        //    {
        //        criteria.Add(criterium);
        //    }
        //    if (sortOrderList != null)
        //    {
        //        foreach (Order ordr in sortOrderList)
        //        {
        //            criteria.AddOrder(ordr);
        //        }
        //    }

        //    return criteria.List<T>() as List<T>;
        //}


        public List<T> GetAllForGridView(ref GridViewData grdData)
        {
            try
            {
                ICriteria criteria = NHibernateSession.CreateCriteria(persitentType);

                ICriterion oCriterionFinal = null;
                if (grdData.Criteria.Count > 0)
                {
                    int nCount = 0;
                    foreach (GVEntityData oGVE in grdData.Criteria)
                    {
                        ICriterion oNewCriterion = null;
                        if (oGVE.Type.Equals(CriteriaDataType.String))
                        {
                            if (oGVE.SearchType == CriteriaSearchType.Contains)
                            {
                                oNewCriterion = Expression.Like(oGVE.Name, oGVE.Value[0], MatchMode.Anywhere);
                            }
                            else if (oGVE.SearchType == CriteriaSearchType.StartsWith)
                            {
                                oNewCriterion = Expression.Like(oGVE.Name, oGVE.Value[0], MatchMode.Start);
                            }
                            else if (oGVE.SearchType == CriteriaSearchType.EndsWith)
                            {
                                oNewCriterion = Expression.Like(oGVE.Name, oGVE.Value[0], MatchMode.End);
                            }
                            else if (oGVE.SearchType == CriteriaSearchType.Equals)
                            {
                                oNewCriterion = Expression.Eq(oGVE.Name, oGVE.Value[0]);
                            }
                            else if (oGVE.SearchType == CriteriaSearchType.In)
                            {
                                string[] paramVals = new string[oGVE.Value.Count];
                                int iCount = 0;
                                foreach (string sVal in oGVE.Value)
                                {
                                    paramVals[iCount] = sVal;
                                    iCount++;
                                }
                                oNewCriterion = Expression.In(oGVE.Name, paramVals);
                            }

                            else if (oGVE.SearchType == CriteriaSearchType.NotIn)
                            {
                                string[] paramVals = new string[oGVE.Value.Count];
                                int iCount = 0;
                                foreach (string sVal in oGVE.Value)
                                {
                                    paramVals[iCount] = sVal;
                                    iCount++;
                                }
                                oNewCriterion = Expression.Not(Expression.In(oGVE.Name, paramVals));
                            }


                        }
                        else if (oGVE.Type.Equals(CriteriaDataType.Number))
                        {
                            if (oGVE.SearchType == CriteriaSearchType.LessThan)
                            {
                                oNewCriterion = Expression.Le(oGVE.Name, int.Parse(oGVE.Value[0]));
                            }
                            else if (oGVE.SearchType == CriteriaSearchType.GreaterThan)
                            {
                                oNewCriterion = Expression.Ge(oGVE.Name, int.Parse(oGVE.Value[0]));
                            }
                            else if (oGVE.SearchType == CriteriaSearchType.Equals)
                            {
                                oNewCriterion = Expression.Eq(oGVE.Name, int.Parse(oGVE.Value[0]));
                            }
                            else if (oGVE.SearchType == CriteriaSearchType.Between)
                            {
                                oNewCriterion = Expression.Between(oGVE.Name, int.Parse(oGVE.Value[0]), int.Parse(oGVE.Value[1]));
                            }


                            else if (oGVE.SearchType == CriteriaSearchType.In)
                            {
                                int[] paramVals = new int[oGVE.Value.Count];
                                int iCount = 0;
                                foreach (string sVal in oGVE.Value)
                                {
                                    paramVals[iCount] = int.Parse(sVal);
                                    iCount++;
                                }
                                oNewCriterion = Expression.In(oGVE.Name, paramVals);
                            }

                            else if (oGVE.SearchType == CriteriaSearchType.NotIn)
                            {
                                int[] paramVals = new int[oGVE.Value.Count];
                                int iCount = 0;
                                foreach (string sVal in oGVE.Value)
                                {
                                    paramVals[iCount] = int.Parse(sVal);
                                    iCount++;
                                }
                                oNewCriterion = Expression.Not(Expression.In(oGVE.Name, paramVals));
                            }
                        }
                        else if (oGVE.Type.Equals(CriteriaDataType.DateTime))
                        {
                            if (oGVE.SearchType == CriteriaSearchType.LessThan)
                            {
                                oNewCriterion = Expression.Le(oGVE.Name, DateTime.Parse(oGVE.Value[0]));
                            }
                            else if (oGVE.SearchType == CriteriaSearchType.GreaterThan)
                            {
                                oNewCriterion = Expression.Ge(oGVE.Name, DateTime.Parse(oGVE.Value[0]));
                            }
                            else if (oGVE.SearchType == CriteriaSearchType.Equals)
                            {
                                oNewCriterion = Expression.Eq(oGVE.Name, DateTime.Parse(oGVE.Value[0]));
                            }
                            else if (oGVE.SearchType == CriteriaSearchType.Between)
                            {
                                oNewCriterion = Expression.Between(oGVE.Name, DateTime.Parse(oGVE.Value[0]), DateTime.Parse(oGVE.Value[1]));
                            }
                        }

                        if (oCriterionFinal != null)
                        {
                            if (grdData.Criteria[nCount - 1].RelationWithSibling.Equals(CriteriaRelationWithSibling.And))
                            {
                                oCriterionFinal = Expression.And(oCriterionFinal, oNewCriterion);
                            }
                            else
                            {
                                oCriterionFinal = Expression.Or(oCriterionFinal, oNewCriterion);
                            }
                        }
                        else
                        {
                            oCriterionFinal = oNewCriterion;
                        }

                        nCount++;
                    }
                }

                if (oCriterionFinal != null)
                {
                    criteria.Add(oCriterionFinal);
                }

                //Add sorting
                bool sOrder = grdData.SortOrder.Equals("ASC") ? true : false;
                Order sortOrder = new Order(grdData.SortColumn, sOrder);
                //Take count
                grdData.TotalRecords = criteria.List<T>().Count;
                //Add order
                criteria.AddOrder(sortOrder);
                return criteria.SetMaxResults(grdData.PageSize).SetFirstResult((grdData.PageIndex - 1) * grdData.PageSize).List<T>() as List<T>;
            }
            catch (Exception ex)
            {
                logger.Error("Error while getting records by criteria .", ex);
                return null;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sSortColumn"></param>
        /// <param name="sDirection"></param>
        /// <returns></returns>
        public bool GetByMyQuery(string sQuery, ref List<T> oList, ref GridViewData grdData)
        {
            try
            {
                sQuery = sQuery.Replace("*TABLENAME*", persitentType.Name);
                logger.Debug("Query : " + sQuery);

                //Add sorting
                bool sOrder = grdData.SortOrder.Equals("ASC") ? true : false;
                Order sortOrder = new Order(grdData.SortColumn, sOrder);
                //Take count
                if(sOrder)
                    sQuery = sQuery + " order by " + grdData.SortColumn;
                else
                    sQuery = sQuery + " order by " + grdData.SortColumn + " desc";
                IQuery iQuery = NHibernateSession.CreateQuery(sQuery);
                grdData.TotalRecords = iQuery.List<T>().Count;
                if (grdData.TotalRecords <= (grdData.PageIndex - 1) * grdData.PageSize)
                {
                    if (grdData.TotalRecords > 0)
                    {
                        grdData.PageIndex -= 1;
                    }
                }
                iQuery = NHibernateSession.CreateQuery(sQuery).SetMaxResults(grdData.PageSize).SetFirstResult((grdData.PageIndex - 1) * grdData.PageSize);

                oList = iQuery.List<T>() as List<T>;
                return true;
            }
            catch (Exception ex)
            {
                logger.Error("Error while running full text query .", ex);
                return false;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sSortColumn"></param>
        /// <param name="sDirection"></param>
        /// <returns></returns>
        public int ExecuteQuery(string sQuery)
        {
            try
            {
                sQuery = sQuery.Replace("*TABLENAME*", persitentType.Name);
                logger.Debug("Query : " + sQuery);
                IQuery iQuery = NHibernateSession.CreateQuery(sQuery);                
                iQuery = NHibernateSession.CreateQuery(sQuery);                
                return iQuery.ExecuteUpdate();
            }
            catch (Exception ex)
            {
                logger.Error("Error while running full text query .", ex);
                return 0;
            }
        }
        /// <summary>
        /// Get by simple Query
        /// </summary>
        /// <param name="maxResults"></param>
        /// <param name="sQuery"></param>
        /// <returns></returns>
        public List<T> GetBySimpleQuery(int maxResults, string sQuery)
        {
            try
            {
                sQuery = sQuery.Replace("*TABLENAME*", persitentType.Name);
                logger.Debug("Query : " + sQuery);

                IQuery iQuery = NHibernateSession.CreateQuery(sQuery).SetMaxResults(maxResults);
                return iQuery.List<T>() as List<T>;
            }
            catch (Exception ex)
            {                
                return null;
            }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="Query"></param>
        /// <returns></returns>
        public DataSet GetDataSetByQuery(string Query)
        {
            try
            {
                DataSet oDataSet = new DataSet();
                System.Data.IDbCommand command = NHibernateSession.Connection.CreateCommand();
                oDataSet.Load(command.ExecuteReader(), LoadOption.OverwriteChanges, "Table0");               
                return oDataSet;
            }
            catch (Exception ex)
            {
                logger.Error("Get dataset by executing a query.", ex);
                return null;
            }
        }
        private Type persitentType = typeof(T);
        private ILogger logger;
        protected const int defaultMaxResults = 100000;
    }
}
